package com.fast.tcc.service;

import com.fast.tcc.controller.StatusCode;
import com.fast.tcc.exception.PartialConfirmException;
import com.fast.tcc.exception.ReservationExpireException;
import com.fast.tcc.model.Participant;
import com.fast.tcc.model.TccErrorResponse;
import com.fast.tcc.model.TccRequest;
import com.fast.tcc.model.TccStatus;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.wmm.common.Shift;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.OffsetDateTime;
import java.util.List;

/**
 * @Auther: wangzun
 * @Date: 2018/9/14 15:08
 * @Description:
 */
@Service
@Slf4j
public class CoordinateService {

    private final RestTemplate restTemplate;

    private static final HttpEntity<?> REQUEST_ENTITY;

    //加载前构造 设置HttpHeader为Application/json
    static {
        final HttpHeaders header = new HttpHeaders();
        header.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON_UTF8));
        header.setContentType(MediaType.APPLICATION_JSON_UTF8);
        REQUEST_ENTITY = new HttpEntity<>(header);
    }

    //在加载时构造RestTemplate
    @Autowired
    public CoordinateService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }


    public void confrim(TccRequest request) {
        Preconditions.checkNotNull(request);
        final List<Participant> participantLinks = request.getParticipantList();
        Preconditions.checkNotNull(participantLinks);
        //调用资源确认链接
        int success = 0;
        int faild = 0;
        for (Participant participantLink : participantLinks) {
            participantLink.setExecuteTime(OffsetDateTime.now());
            //必须设置重试防止网络抖动情况或者服务宕机
            final ResponseEntity<String> response = restTemplate.exchange(participantLink.getUri(), HttpMethod.PUT, REQUEST_ENTITY, String.class);
            if(response.getStatusCode() == HttpStatus.NO_CONTENT) {
                participantLink.setTccStatus(TccStatus.CONFIRMED);
                success++;
            }else if(response.getStatusCode() == HttpStatus.NOT_FOUND) {
                participantLink.setTccStatus(TccStatus.TIMEOUT);
                participantLink.setParticipantErrorResponse(response);
                faild++;
            }else {
                Shift.fatal(StatusCode.SERVER_UNKNOWN_ERROR,response);
            }

        }
        //检查业务执行是否有冲突情况  .只能全部成功或者全部失败
        if(success > 0 && faild > 0) {
            throw new PartialConfirmException("all reservation were cancelled or timeout", new TccErrorResponse(participantLinks));
        }else if(faild == participantLinks.size()) {
            throw new ReservationExpireException("although we have check the expire time in request body, we got an expiration when confirming actually");
        }
    }

    public void cancel(TccRequest request) {
        Preconditions.checkNotNull(request);
        final List<Participant> participants = request.getParticipantList();
        Preconditions.checkNotNull(participants);

        try {
            for (Participant participant : participants) {
                restTemplate.exchange(participant.getUri(), HttpMethod.DELETE, null, String.class);
            }
        }catch (Exception e) {
            log.error("unexpected error when making compensation: {}", e.toString());
        }
    }
}
